<!--
/*
 * 文件上传
 */
使用方法
  <upload-file :uploadId="uploadFileId" :multiple="0" :limit="5" :files="fileList" @getFiles="getFiles"></upload-file>

  属性：
    uploadId 组件ID
    fileList 必传，文件数组 格式为[{id:文件id,url:文件路径}]
    multiple 是否支持多选文件 0为false 1为true
    limit 最大允许上传个数
  方法：
    @getFiles="fn" 必传，子组件传值，得到value并赋值给图片资源
      fn(value){
        this.file = value;
      },
-->
<template>
  <el-container>
    <el-main>
      <div>
        <el-upload
          ref="uploadId"
          action="/"
          :http-request="handleUpload"
          :on-preview="handlePictureCardPreview"
          :on-remove="handleRemove"
          :show-file-list="showList"
          :multiple="multiple"
          :file-list="fileList">
          <el-button :size="btn.size||'small'" :type="btn.type||'primary'">{{btn.text||'点击上传'}}</el-button>
          <!--<div slot="tip" class="el-upload__tip">只能上传jpg/png文件，且不超过500kb</div>-->
        </el-upload>
      </div>
    </el-main>
  </el-container>
</template>

<script>
  import SparkMD5 from 'spark-md5';
  import * as api from '@/api/common/upload';

  export default {
    name: 'UploadFile',
    props: {
      uploadId: {
        type: String,
        default: 'upload-file'
      },
      multiple: {
        type: Boolean,
        default: true
      },
      files: {
        type: Array,
        default: () => []
      },
      showList: {
        type: Boolean,
        default: false
      },
      btn: {
        type: Object,
        default: () => ({})
      }
    },
    data() {
      return {
        baseUrl: process.env.BASE_FILE_URL,
        fileList: this.files,
        // uploadShow: true,
        uploading: false,
        maxSize: 20 * 1024 * 1024, // 上传最大文件限制-20M
        multiUploadSize: 10 * 1024 * 1024, // 大于这个大小的文件使用分块上传(后端可以支持断点续传)-10M
        chunkSize: 1 * 1024 * 1024 // 每块文件大小-1M
      };
    },
    mounted() {
      // this.fileList = this.getList(this.files).slice();
      // this.fileList = this.file;
      // this.$emit('getFiles', this.fileList,this.uploadId);
    },
    watch: {
      files(value, old) {
        this.fileList = value.slice(); // 不能注释
      }
    },
    methods: {
      async handleUpload(options) {
        console.log('Handle Upload, ', options);
        const {
          uploading,
          maxSize,
          chunkSize,
          getSize
        } = this;
        const {
          file,
          onProgress,
          onSuccess,
          onError
        } = options;
        if (uploading) return;
        if (file.size > maxSize) {
          return this.$message({
            message: `您选择的文件大于${getSize(maxSize)}`,
            type: 'error'
          });
        }
        let currentChunk = 0;
        // this.uploading = true
        this.getFileMD5(file, async md5 => {
          console.log('MD5: ', md5);
          // 先检查文件是否已经在上传
          const checkRes = await this.checkFile({
            md5
          });
          console.log('check res:', checkRes);
          if (checkRes.code === 200) {
            if (checkRes.data.file) {
              this.appendList(checkRes.data.file, md5);
              // debugger
              this.$emit('getFiles', this.fileList, this.uploadId);
              return;
            }
            currentChunk = checkRes.data.chunkCurr;
            // 声明必要的变量
            const blobSlice =
              File.prototype.slice ||
              File.prototype.mozSlice ||
              File.prototype.webkitSlice;
            const fileReader = new FileReader();
            const postParam = {
              md5,
              chunk: 0,
              chunks: Math.ceil(file.size / chunkSize), // 计算分割的块数
              chunkSize,
              name: file.name,
              size: file.size,
              type: file.type,
              uid: file.uid,
              currentPercent: 0
            };
            // 每块文件读取完毕之后的处理
            fileReader.onload = async e => {
              // 文件块上传
              if (postParam.chunk === currentChunk) {
                console.log('chunkSize: ', chunkSize);
                postParam.file = new Blob([e.target.result]);
                const uploadRes = await this.postFile(postParam, onProgress);
                console.log('upload res: ', uploadRes);
                currentChunk++;
                // 如果还有分片则继续处理
                if (currentChunk < postParam.chunks) {
                  loadNext();
                } else {
                  // 上传完成
                  console.log('上传完成');
                  postParam.currentPercent = 0;
                  this.uploading = false;
                  onProgress({
                    percent: 100
                  });
                  this.appendList(uploadRes.data, md5);
                  this.$emit('getFiles', this.fileList, this.uploadId);
                  // this.dialogImageUrl = `${this.baseUrl}/${uploadRes.data.fileUrl}`;
                }
              }
              postParam.chunk++;
            };

            // 处理文件读取错误
            fileReader.onerror = () => {
              console.warn('oops, something went wrong.');
            };

            // 处理单片文件的上传
            const loadNext = () => {
              const start = currentChunk * chunkSize;
              const end =
                start + chunkSize >= file.size ? file.size : start + chunkSize;

              fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
            };

            loadNext();
          } else {
            this.$message({
              message: '文件校验失败',
              type: 'error'
            });
          }
        });
      },

      // 格式化文件大小显示文字
      getSize(size) {
        return size > 1024 ?
          size / 1024 > 1024 ?
            size / (1024 * 1024) > 1024 ?
              (size / (1024 * 1024 * 1024)).toFixed(2) + 'GB' :
              (size / (1024 * 1024)).toFixed(2) + 'MB' :
            (size / 1024).toFixed(2) + 'KB' :
          size.toFixed(2) + 'B';
      },

      // 提交文件方法,将参数转换为FormData, 然后通过axios发起请求
      async postFile(param, onProgress) {
        const formData = new FormData();
        for (const p in param) {
          console.log(p);
          formData.append(p, param[p]);
        }
        const config = {
          onUploadProgress: e => {
            // 调用upload组件的onProgress方法会读取e.percent来作为用进度条的数值, 所以直接赋值再传入即可
            e.percent =
              param.currentPercent +
              Number(
                (e.loaded / e.total * (1 / (param.chunks || 1)) * 100).toFixed(2)
              );
            param.currentPercent = e.percent;
            onProgress(e);
          }
        };
        return api.upload(formData, config).then(rs => rs);
        // return axios.post('http://47.97.184.199:8765/api/dfs/file/upload', formData, config).then(rs => rs.data)
      },

      // 文件校验
      async checkFile(param) {
        const formData = new FormData()
        for (const p in param) {
          formData.append(p, param[p])
        }
        return api.check(formData).then(rs => rs)
        // return axios.post('http://47.97.184.199:8765/api/dfs/file/check', formData).then(rs => rs.data)
      },
      handleRemove(file, fileList) {
        // 通过name去匹配相应的文件进行删除
        if (file.raw) {
          this.getFileMD5(file.raw, async md5 => {
            let hasRemove = false;
            this.fileList = this.fileList.filter(image => {
              if (image.name === md5 && !hasRemove) {
                hasRemove = true
                return false
              }
              return true;
            });
            this.$emit('getFiles', this.fileList, this.uploadId)
          });
          return false;
        } else {
          let hasRemove = false;
          this.fileList = this.fileList.filter(image => {
            if (image.name === file.name && !hasRemove) {
              hasRemove = true
              return false
            }
            return true;
          });
          this.$emit('getFiles', this.fileList, this.uploadId)
        }
      },
      handlePictureCardPreview(file) {
        this.dialogImageUrl = file.url
        this.dialogVisible = true
      },
      // 获取文件MD5值
      getFileMD5(file, callback) {
        // 声明必要的变量
        const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
        const fileReader = new FileReader()
        // 文件每块分割2M，计算分割详情
        const chunkSize = this.chunkSize
        const chunks = Math.ceil(file.size / chunkSize)
        let currentChunk = 0
        // 创建md5对象（基于SparkMD5）
        const spark = new SparkMD5.ArrayBuffer()
        // 每块文件读取完毕之后的处理
        fileReader.onload = e => {
          // 每块交由sparkMD5进行计算
          spark.append(e.target.result)
          currentChunk++

          // 如果文件处理完成计算MD5，如果还有分片继续处理
          if (currentChunk < chunks) {
            loadNext();
          } else {
            callback(spark.end());
          }
        };

        // 处理文件读取错误
        fileReader.onerror = () => {
          console.warn('oops, something went wrong.');
        };

        // 处理单片文件的上传
        const loadNext = () => {
          const start = currentChunk * chunkSize;
          const end =
            start + chunkSize >= file.size ? file.size : start + chunkSize;

          fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
        };

        loadNext();
      },
      appendList(file, md5) {
        // const image = {
        //   id: file.id,
        //   name: md5,
        //   url: `${this.baseUrl}/${file.fileUrl}`,
        //   relativeUrl: file.fileUrl
        // }
        console.log('appendList============', file);
        this.fileList.push(file)
      },
      getList(files) {
        console.log('file============', files);
        const list = [];
        for (let i = 0; i < files.length; i++) {
          const name = files[i].url.split('/').pop()
          list.push({
            id: files[i].id,
            name,
            url: files[i].url.indexOf(this.baseUrl) > -1 ? files[i].url : `${this.baseUrl}/${files[i].url}`, // url: `${this.baseUrl}/${files[i].url}`,
            relativeUrl: files[i].url
          });
        }
        return list;
      },
      clearFiles() {
        this.fileList = [];
        this.$refs.uploadFile.clearFiles()
      }
    }
  };
</script>

<style scoped>
</style>
